﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XingLucifer.Devices.Exceptions;

namespace XingLucifer.Devices.Protocols
{
    public class TCP_OmronCIP_Protocol : IBase.IProtocol
    {
        private byte[] _sessionHandle;
        private byte _slotNumber;

        public TCP_OmronCIP_Protocol(byte slotNumber, byte[] session)
        {
            _slotNumber = slotNumber;
            _sessionHandle = session;
        }

        #region CIP
        private byte[] _header = new byte[24]
        {
             0x6F,0x00,
              0x28,0x00,
              0x00,0x00,0x00,0x00,
              0x00,0x00,0x00,0x00,
              0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
              0x00,0x00,0x00,0x00,
        };

        private byte[] _commandSpecificData = new byte[16]
        {
               0x00,0x00,0x00,0x00,
                0x01,0x00,
                0x02,0x00,
                0x00,0x00,
                0x00,0x00,
                0xb2,0x00,
                0x00,0x00,
        };

        private byte[] _cipHeader = new byte[]
        {
            0x52,0x02,
            0x20,0x06,0x24,0x01,
            0x0A,0xF0,
            0x00,0x00,
        };
        #endregion

        public byte[] Deserialize(IList<byte> data)
        {
            if (data.Count < 44)
            {
                throw new Exception("不满足正常CIP报文");
            }
            string error = GetStatusCode(data[42]);
            if (error != "Success")
            {
                throw new Exception($"CIP协议报错：{error}");
            }
            return data.Skip(44).ToArray();
        }

        public byte[] Serialize(IList<byte> data)
        {
            if (_sessionHandle == null)
            {
                throw new Exception("会话未注册！！！");
            }
            List<byte> result = new List<byte>();
            int command_length = _cipHeader.Length + data.Count + 4;
            int protocol_length = _commandSpecificData.Length + command_length;
            _header[2] = (byte)protocol_length;
            _header[3] = (byte)(protocol_length >> 8);
            _header[4] = _sessionHandle[0];
            _header[5] = _sessionHandle[1];
            _header[6] = _sessionHandle[2];
            _header[7] = _sessionHandle[3];
            _commandSpecificData[14] = (byte)command_length;
            _commandSpecificData[15] = (byte)(command_length >> 8);
            _cipHeader[8] = (byte)(data.Count);
            _cipHeader[9] = (byte)(data.Count >> 8);
            result.AddRange(_header);
            result.AddRange(_commandSpecificData);
            result.AddRange(_cipHeader);
            result.AddRange(data);
            result.Add(0x01);
            result.Add(0x00);
            result.Add(0x01);
            result.Add(_slotNumber);
            return result.ToArray();
        }

        public bool Verify(IList<byte> bytes)
        {
            if (bytes.Count < 4)
            {
                System.Threading.Thread.Sleep(2);
                return false;
            }
            int length = (bytes[2] + (bytes[3] << 8)) + 24;

            if (length > bytes.Count)
            {
                System.Threading.Thread.Sleep(2);
                return false;
            }
            if (bytes[8] != 0 && bytes[9] != 0)
            {
                throw new CIPHeaderException(new byte[] { bytes[8], bytes[9] });
            }
            return true;
        }

        private string GetStatusCode(byte code)
        {
            switch (code)
            {
                case 0x00: return "Success";
                case 0x01: return "Connection failure";
                case 0x02: return "Resource unavailable";
                case 0x03: return "Invalid Parameter value";
                case 0x04: return "Path segment error";
                case 0x05: return "Path destination unknown";
                case 0x06: return "Partial transfer";
                case 0x07: return "Connection lost";
                case 0x08: return "Service not supported";
                case 0x09: return "Invalid attribute value";
                case 0x0A: return "Attribute List error";
                case 0x0B: return "Already in requested mode/state";
                case 0x0C: return "Object state conflict";
                case 0x0D: return "Object already exists";
                case 0x0E: return "Attribute not settable";
                case 0x0F: return "Privilege violation";
                case 0x10: return "Device state conflict";
                case 0x11: return "Reply data too large";
                case 0x12: return "Fragmentation of a primitive value";
                case 0x13: return "Not enough data";
                case 0x14: return "Attribute not supported";
                case 0x15: return "Too much data";
                case 0x16: return "Object does not exist";
                case 0x17: return "Service fragmentation sequence not in progress";
                case 0x18: return "No stored attribute data";
                case 0x19: return "Store operation failure";
                case 0x1A: return "Routing failure, request packet too large";
                case 0x1B: return "Routing failure, response packet too large";
                case 0x1C: return "Missing attribute list entry data";
                case 0x1D: return "Invalid attribute value list";
                case 0x1E: return "Embedded service error";
                case 0x1F: return "Vendor specific error";
                case 0x20: return "Invalid parameter";
                case 0x21: return "Write-once value or medium atready written";
                case 0x22: return "Invalid Reply Received";
                case 0x23: return "Buffer overflow";
                case 0x24: return "Message format error";
                case 0x25: return "Key failure path";
                case 0x26: return "Path size invalid";
                case 0x27: return "Unecpected attribute list";
                case 0x28: return "Invalid Member ID";
                case 0x29: return "Member not settable";
                case 0x2A: return "Group 2 only Server failure";
                case 0x2B: return "Unknown Modbus Error";
                default: return "unknown";
            }
        }
    }
}
